package ie.ucd.ABLE.MachineLearning;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;

import weka.core.Instances;

public class StepChangeDetection {

	private ArrayList<CurrentCostData> CurrentCostSensorData;
	private ArrayList<ApplianceEventQueue> ApplianceEventList;
	private MySQLConnect mySQLDB;
	private int DATA_COUNTER;
	private int LIMIT_DATA_LOADING;
	private int THRESH_HOLD_PLUS, THRESH_HOLD;
	private int THRESH_HOLD_MINUS;
	private java.util.Calendar cal;


	public StepChangeDetection(){

		DATA_COUNTER = 0;
		LIMIT_DATA_LOADING = 1000000;
		THRESH_HOLD_PLUS = 80;
		THRESH_HOLD_MINUS = -80;
		THRESH_HOLD = 100;
		slidingWindowStartup=10;
		slidingWindow=new ArrayList<Double>(); 
		slidingWindowSize=0;
		cps = new ArrayList<Integer>();
		dts = new ArrayList<Integer>();
		CurrentCostSensorData = new ArrayList<CurrentCostData>();
		ApplianceEventList = new ArrayList<ApplianceEventQueue>();
		//set Timezone to GMT
		cal = Calendar.getInstance();
		cal.setTimeZone(TimeZone.getTimeZone("GMT"));
		//		connectWithMysqlServer();



	}

	public void connectWithMysqlServer(){
		//Connect to My SQL Server...
		mySQLDB = new MySQLConnect("able1.ucd.ie", "able_db", "ableDbP@ssword", "able");

		if (mySQLDB.getDbConnection()==null){
			mySQLDB = new MySQLConnect("able1.ucd.ie", "able_db", "ableDbP@ssword", "able");
		}else{
			System.out.println("Connected to MySQL Server...");
		}		
	}

	public void retreiveRecordsAndFillArrayList(String query, String sensorType){
		//to check memory usage of java... debugging in case memory crashes...
		MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
		int MEGABYTE = (1024*1024);
		//variable declaration to be used while retreiving mysql data...
		ResultSet resultS;
		double SensedValue; 
		Timestamp SenseDate = null; 
		int Temperature;
		try {		
			resultS = mySQLDB.queryTable(query);
			if(sensorType.equalsIgnoreCase("CurrentCost")){	//if its epiSensor do this
				DATA_COUNTER = 0;
				if(resultS.next()){
					do {
						SenseDate = resultS.getTimestamp("SenseDate", cal);
						SensedValue = resultS.getInt("SenseValue");
						Temperature = resultS.getInt("temp");
						CurrentCostSensorData.add(new CurrentCostData(SenseDate, 0, (int)SensedValue, Temperature));
						DATA_COUNTER++;
					}while (resultS.next() && DATA_COUNTER < LIMIT_DATA_LOADING); //Retreive Not more than 10,00,000 records at a time...
					resultS.close(); //close result set now...
					System.out.println(DATA_COUNTER+ " Episensor unlabeled data retrieved...");
				}else{
					System.out.println("No EpiSensor data to be retrieved...");
				}
			}else{
				System.out.println("Nothing retreived...");		//if its neither of above	
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}catch (OutOfMemoryError e){
			MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
			long maxMemory = heapUsage.getMax() / MEGABYTE;
			long usedMemory = heapUsage.getUsed() / MEGABYTE;
			System.out.println(" : Memory Used :" + usedMemory + "M/" + maxMemory + "M");
		}
	}

	public void readFromCSV(){

		try
		{
			FileReader fr = new FileReader("current_cost_training.csv");
			BufferedReader br = new BufferedReader(fr);
			String stringRead = br.readLine();

			while( stringRead != null )
			{
				//				System.out.println(stringRead);
				String[] elements = stringRead.split(",");

				if(elements.length < 3) {
					throw new RuntimeException("line too short"); //handle missing entries
				}

				String dTime = elements[0];
				String sensorValue = elements[1];
				String applianceActive = elements[2];

				CurrentCostSensorData.add(new CurrentCostData(Timestamp.valueOf(dTime),0,0,Integer.valueOf(sensorValue)));

				// read the next line
				stringRead = br.readLine();
			}
			br.close( );
		}
		catch(IOException ioe){
		}
	}



	// ================================= version 001 ================================== //

	public void findStepChangeVersion001(){
		int eventCheck, countEvent=0;

		for (int i = 0; i < CurrentCostSensorData.size()-1; i++) {
			eventCheck = CurrentCostSensorData.get(i+1).getSenseValue()-CurrentCostSensorData.get(i).getSenseValue();
			if(eventCheck >= THRESH_HOLD_PLUS || eventCheck < THRESH_HOLD_MINUS){ // check if the there is a gap
				System.out.println("EVENT DETECTED BETWEEN: "+CurrentCostSensorData.get(i).getDateTime()+" AND "+CurrentCostSensorData.get(i+1).getDateTime()+" VALUES: "+eventCheck);
				countEvent++;
			}
		}
		System.out.println(countEvent);
	}



	// ================================= version 002 ================================== //

	public void findStepChangeVersion002(){
		int baseLine, RealPowerSum = 0, eventCount=0, ithIndex = 0;
		boolean eventStarted = false, captureiThIndex = true, eventFinished = false;
		Timestamp eventStartTime=null, eventEndTime=null;
		baseLine = 80; //A starter and then calculate as an average for 10 readings assuming that an appliance is turned on for at least half a minute.
		for (int i = 0; i < CurrentCostSensorData.size()-1; i++) {
			if(eventStarted==false){
				if(CurrentCostSensorData.get(i).getSenseValue()-baseLine >= THRESH_HOLD){
					eventStartTime = CurrentCostSensorData.get(i).getDateTime();
					eventStarted = true;
					eventFinished = false; // to be used for fixing ithIndex value...
					captureiThIndex = true; // to be used to allow recording ithIndex value...
					baseLine = CurrentCostSensorData.get(i).getSenseValue();
					System.out.println("\nEvent started: "+eventStartTime);
				}else{
					//recalculate baseline here...an average for stable readings 
					if(captureiThIndex){
						if(eventFinished){
							ithIndex = i-1;
						}else{
							ithIndex = i;
						}
						captureiThIndex = false;
					}
					RealPowerSum = 0;
					for (int j = ithIndex; j <= i; j++) {
						RealPowerSum = RealPowerSum + CurrentCostSensorData.get(j).getSenseValue();
					}baseLine = RealPowerSum/(i+1-ithIndex);
				}

			}else{ // what if another event starts after an event already started...?				
				if(CurrentCostSensorData.get(i).getSenseValue()-baseLine < -THRESH_HOLD){
					eventEndTime = CurrentCostSensorData.get(i).getDateTime();
					eventStarted = false;
					captureiThIndex = true;
					eventFinished = true; // to be used for fixing ithIndex value...
					baseLine = CurrentCostSensorData.get(i).getSenseValue();
					eventCount++;
					System.out.println("Event finished: "+eventEndTime);
				}else{
					//recalculate baseline here...an average for steady readings 
					if(captureiThIndex){
						ithIndex = i-1; // because this is in else block and it will execute in next iteration
						captureiThIndex = false;
					}
					RealPowerSum = 0;
					for (int j = ithIndex; j <= i; j++) {
						RealPowerSum = RealPowerSum + CurrentCostSensorData.get(j).getSenseValue();
					}baseLine = RealPowerSum/(i+1-ithIndex);
				}			
			}
		}
		System.out.println(eventCount); // print number of event detected
	}



	// ================================= version 003 ================================== //

	public void findStepChangeVersion003(){
		//inner class which will create a seperate thread for each appliance...
		class NewApplianceThread extends Thread{

			Timestamp eventStartTime, eventEndTime;
			int startIndex;
			int baseLine;
			boolean loopTrue;

			public NewApplianceThread(Timestamp eStartTime, int sIndex, int bLine){				
				eventStartTime = eStartTime;
				startIndex = sIndex;
				baseLine = bLine;
				loopTrue = true; 
			}

			@Override
			public void run() {
				int i = startIndex;
				while(loopTrue){
					if(CurrentCostSensorData.get(i).getSenseValue()-baseLine < -THRESH_HOLD){
						eventEndTime = CurrentCostSensorData.get(i).getDateTime();
						loopTrue=false;
					}
					i++;	
				}
			}

		}


		int baseLine, RealPowerSum = 0, eventCount=0, ithIndex = 0;
		Timestamp eventStartTime=null, eventEndTime=null;
		baseLine = 80; //A starter and then calculate as an average for 10 readings assuming that an appliance is turned on for at least half a minute.

		for (int i = 0; i < CurrentCostSensorData.size()-1; i++) {


			if(CurrentCostSensorData.get(i).getSenseValue()-baseLine >= THRESH_HOLD){
				eventStartTime = CurrentCostSensorData.get(i).getDateTime();
				System.err.println("\nEvent started: "+eventStartTime+"\n");
				captureNewEvent(eventStartTime, i, baseLine);
				baseLine = CurrentCostSensorData.get(i).getSenseValue();
				ithIndex = i;
				eventCount++;
			}else if(CurrentCostSensorData.get(i).getSenseValue()-baseLine < -THRESH_HOLD){
				eventEndTime = CurrentCostSensorData.get(i).getDateTime();
				baseLine = CurrentCostSensorData.get(i).getSenseValue();
				ithIndex = i;
				System.err.println("\nEvent finished: "+eventEndTime);
			}else{
				//recalculate baseline here...an average for stable readings 
				RealPowerSum = 0;
				for (int j = ithIndex; j <= i; j++) {
					RealPowerSum = RealPowerSum + CurrentCostSensorData.get(j).getSenseValue();
				}baseLine = RealPowerSum/(i+1-ithIndex);
			}			
		}
		System.out.println(eventCount); // print number of event detected
	}

	public void captureNewEvent(Timestamp eStartTime, int sIndex, int bLine){
		int i = sIndex;
		int baseLine = CurrentCostSensorData.get(i).getSenseValue();
		Timestamp eventEndTime;
		boolean loopTrue = true;
		while(loopTrue){
			if(CurrentCostSensorData.get(i).getSenseValue()-baseLine < -THRESH_HOLD){
				eventEndTime = CurrentCostSensorData.get(i).getDateTime();
				loopTrue=false;
			}else{
				//recalculate baseline here...an average for stable readings 
				int RealPowerSum = 0;
				for (int j = sIndex; j <= i; j++) {
					RealPowerSum = RealPowerSum + CurrentCostSensorData.get(j).getSenseValue();
				}baseLine = RealPowerSum/(i+1-sIndex);
				// for testing...
				int realPowerValue = CurrentCostSensorData.get(i).getSenseValue()-bLine;
				System.out.println("Date: "+CurrentCostSensorData.get(i).getDateTime()+" Value: "+realPowerValue);
			}

			i++;	
		}
	}

	// ================================= version 004 ================================== //
	// something like...   if (t0 >= tValue && !beginFound) { and   if (t0 >= tValue && beginFound) { this is multi appliance... 



	public void findStepChangeVersion004(){
		List<Integer> stepChangeList = new ArrayList<Integer>();
		int baseLine, lastStableBaseLine=0, RealPowerSum = 0, eventCount=0, ithIndex = 0;
		boolean eventStarted = false, calculateBaseLine = true, eventFinished = false;
		Timestamp eventStartTime=null, eventEndTime=null;
		baseLine = 75; //A starter and then calculate as an average for 10 readings assuming that an appliance is turned on for at least half a minute.
		for (int i = 0; i < CurrentCostSensorData.size()-1; i++) {

			if(CurrentCostSensorData.get(i).getSenseValue()-THRESH_HOLD <= THRESH_HOLD){
				//				stepChangeList.add(THRESH_HOLD);
				stepChangeList.add(CurrentCostSensorData.get(i).getSenseValue());
				calculateBaseLine = true;
				ithIndex = i;
			}else if(CurrentCostSensorData.get(i).getSenseValue()-baseLine >= THRESH_HOLD){
				eventStartTime = CurrentCostSensorData.get(i).getDateTime();
				lastStableBaseLine = baseLine;
				stepChangeList.add(baseLine);
				stepChangeList.add(CurrentCostSensorData.get(i).getSenseValue()-baseLine);

				baseLine = CurrentCostSensorData.get(i).getSenseValue();
				ithIndex = i;
				eventCount++;
				System.err.println("\nEvent started: "+eventStartTime+"\n");
				calculateBaseLine = false; 

			}else if(CurrentCostSensorData.get(i).getSenseValue()-baseLine < -THRESH_HOLD){
				eventEndTime = CurrentCostSensorData.get(i).getDateTime();
				baseLine = CurrentCostSensorData.get(i).getSenseValue();
				ithIndex = i;
				System.err.println("\nEvent finished: "+eventEndTime);

			}else{
				calculateBaseLine = true; 
				stepChangeList.add(lastStableBaseLine);
				stepChangeList.add(CurrentCostSensorData.get(i).getSenseValue()-lastStableBaseLine);
			}

			if(calculateBaseLine){
				//recalculate baseline here...an average for stable readings 
				RealPowerSum = 0;
				for (int j = ithIndex; j <= i; j++) {
					RealPowerSum = RealPowerSum + CurrentCostSensorData.get(j).getSenseValue();
				}baseLine = RealPowerSum/(i+1-ithIndex);
			}

			for (int j = 0; j < stepChangeList.size(); j++) {
				System.out.println("Date: "+CurrentCostSensorData.get(i).getDateTime()+" Value: "+stepChangeList.get(j));
			}
			stepChangeList.clear();
		}
		System.out.println(eventCount);
	}


	//////////////////////////////////////////////======== version005: GLR from cpt ========///////////////////////////////////////////////
	private double maxValue;
	private int maxIndex;
	private int slidingWindowStartup;
	private long slidingWindowSize;
	private List<Double> slidingWindow;
	private List<Integer> cps, dts;

	public void findStepChangeVersion005(){
		int dt, cp, i, lastChange=0, size=CurrentCostSensorData.size();
		double threshold=5, temp_obs=0;
		// startup for 5 readings assuming that an appliance is turned on for at least half a minute.
		slidingWindowStartup=2; // just in case if we want any startup: number of streams will not be considered for step change detection.
		i=-1;
		while(i+1<size){
			++i;
			// do update things increase size of window by 1 etc.
			slidingWindowSize++;
			temp_obs = (double) CurrentCostSensorData.get(i).getSenseValue();
			if(!slidingWindow.isEmpty()){ //if the arrayList's size is not zero
				temp_obs=slidingWindow.get(slidingWindow.size()-1).doubleValue() + CurrentCostSensorData.get(i).getSenseValue();
			}
			slidingWindow.add(temp_obs);

			if(slidingWindowSize >= slidingWindowStartup){ //window size = m_startup
				performCalculations(); // calculations on data in sliding window
				if(maxValue > threshold){
					System.out.println(maxValue);
					dt = i+1;
					cp = lastChange + maxIndex+1;
					lastChange = cp;
					i = cp-1;
					dts.add(dt);
					cps.add(cp);
					slidingWindow.clear();
					slidingWindowSize=0;
				}
			}else{
				// do nothing: keep building the startup window
			}		
		}
		//for testing purposes...
		for (int j = 0; j < dts.size(); j++) {
			System.out.println("DTS: "+dts.get(j).intValue());
		}
		for (int j = 0; j < cps.size(); j++) {
			System.out.println("CPS: "+cps.get(j).intValue());
		}
		System.out.println("DTS: "+dts.size()+"\tCPS: "+cps.size());	
	}

	public void performCalculations(){
		int size;
		double n0, n1, s0, s1, K, temp;
		List<Double> stepChangeList = new ArrayList<Double>();
		size = slidingWindow.size();

		for (int i = 0; i < slidingWindow.size(); i++) {
			n0 = (double) i+1;
			n1 = (double) size-n0;
			s0 = (double) slidingWindow.get(i).doubleValue();
			s1 = (double) slidingWindow.get(slidingWindow.size()-1).doubleValue() - s0;
			K = size*Math.log((double)size) - n0*Math.log((double)n0)-n1*Math.log((double)n1);
			temp = -size*Math.log(s0+s1) + n0*Math.log(s0) + n1*Math.log(s1) + K;
			stepChangeList.add(-2*temp); //why -2* ?
			
			System.out.println("n0: "+n0+" \t n1: "+n1+" \t s0: "+s0+"\t s1: "+s1+"\t K: "+Math.round(K * 100.0) / 100.0+" \t temp: "+Math.round(temp * 100.0) / 100.0+" \t -2*temp: "+Math.round((-2*temp) * 100.0) / 100.0);
//			if(-2*temp>5) {  //testing
//				System.out.println("n0: "+n0+" \t n1: "+n1+" \t s0: "+s0+"\t s1: "+s1+"\t K: "+Math.round(K * 100.0) / 100.0+" \t temp: "+Math.round(temp * 100.0) / 100.0+" \t -2*temp: "+Math.round((-2*temp) * 100.0) / 100.0);
//			}
		}
		//		System.out.println("Size of Array is: "+stepChangeList.size());
		// find maxIndex and maxValue among calculated values in sliding window
		maxIndex=0;
		maxValue=0;
		for (int i = 0; i < stepChangeList.size(); i++) {
			if(stepChangeList.get(i).doubleValue()>maxValue){
				maxValue = stepChangeList.get(i).doubleValue();
				maxIndex = i;
			}
		}
	}

	//////////////////////////////////////////////======== version006: T-test based ========///////////////////////////////////////////////
	private int ithIndex=0;
	public void findStepChangeVersion006(){
		int i, size=CurrentCostSensorData.size();
		double temp_obs=0;
		// startup for 5 readings assuming that an appliance is turned on for at least half a minute.
		slidingWindowStartup=10; // just in case if we want any startup: number of streams will not be considered for step change detection.
		List<Double> tempList = new ArrayList<Double>();
		i=0;
		while(i<size-1){
			// do update things increase size of window by 1 etc.
//			System.out.println(slidingWindow.size());
			if(slidingWindow.size() == slidingWindowStartup){ //window size = m_startup
				tempList.clear();
				tempList.addAll(slidingWindow);
				slidingWindow.clear();
				for (int j = 0; j < slidingWindowStartup-1; j++) {
					slidingWindow.add(tempList.get(j+1).doubleValue()); // move the window by one reading
				}slidingWindow.add((double) CurrentCostSensorData.get(i).getSenseValue()); // add new element.
				tempList.clear();
				ithIndex = i+1-slidingWindowStartup/2;
				tTesting(); // calculations on data in sliding window
				if(eventDetected){
					tempList.addAll(slidingWindow);
					slidingWindow.clear();
					slidingWindow.addAll(tempList.subList(slidingWindowStartup/2 -1, tempList.size()));
				}

			}else{
				temp_obs = (double) CurrentCostSensorData.get(i).getSenseValue();
				slidingWindow.add(temp_obs);			
				}
			++i;
		}
		System.out.println("Total events detected: "+stepCount); // total changes found
	}
	
	boolean beginFound = false;
	boolean eventDetected = false;
	int stepCount = 0;
	double beginLevel = 0, tValue=8; // Sensitivity level for first step change
	public void tTesting(){
		//Variables for t-test
		int size;
		double sum1=0, sum2=0, x1Hat, x2Hat, s1, s2, diff, denom, t0, change;
		int switchStart;
		List<Double> stepChangeList = new ArrayList<Double>();
		size = slidingWindow.size();

		// 0. calculating sum1 and sum2
		for (int i = 0; i < slidingWindow.size()/2; i++) {
			sum1 += slidingWindow.get(i).doubleValue();
			sum2 += slidingWindow.get(i+slidingWindow.size()/2).doubleValue();
		}		
		//		System.out.println(sum1+" "+sum2);

		// 1. calculating means
		x1Hat = sum1 / (slidingWindow.size()/2);
		x2Hat = sum2 / (slidingWindow.size()/2);
		//		System.out.println(x1Hat+" "+x2Hat);

		// 2. Calculating sum of squared deviation from mean
		s1 = 0; s2 = 0;
		for (int i=0; i < slidingWindow.size()/2; i++) {
			s1 += (slidingWindow.get(i).doubleValue()-x1Hat) * (slidingWindow.get(i).doubleValue()-x1Hat);
			s2 += (slidingWindow.get(i+slidingWindow.size()/2).doubleValue()-x2Hat) * (slidingWindow.get(i+slidingWindow.size()/2).doubleValue()-x2Hat);  
		}
		//		System.out.println(s1+" "+s2);

		// 3. Dividing squared deviations to get sample variance
		s1 = (s1 / (slidingWindow.size()/2-1)); // adding +1 in order not to run into too high t-values when there is no standard deviation at all...
		s2 = (s2 / (slidingWindow.size()/2-1));
		//		System.out.println(s1+" "+s2);

		// t-Test calculation
		diff = Math.abs(x1Hat - x2Hat); // absolute difference between two means
		denom = Math.sqrt((s1 + s2) / (slidingWindow.size()/2)); // standar error of the mean SEM
		t0 = diff / denom; // t-statistic value

		// testing for mean and t0 both values to see when the change happens
		// this is way better then the orignal one
		if(diff >= 80 && t0 >= tValue){ // diff is here because we dont want to raise a false event alarm...
			System.err.println("\nevent(i): "+ithIndex+"\t Time: "+CurrentCostSensorData.get(ithIndex).getDateTime()+"\t change: "+Math.round((x2Hat - x1Hat) * 100.0) / 100.0+"\t mean1: "+Math.round(x1Hat * 100.0) / 100.0+"\t mean2: "+Math.round(x2Hat * 100.0) / 100.0+"\t s1: "+Math.round(s1 * 100.0) / 100.0+"\t s2: "+Math.round(s2 * 100.0) / 100.0+"\t t0: "+Math.round(t0 * 100.0) / 100.0);
			stepCount += 1;
			eventDetected = true;
		}else{
			change = 0;
			eventDetected =false;
			System.out.println("\nevent(i): "+ithIndex+"\t Time: "+CurrentCostSensorData.get(ithIndex).getDateTime()+"\t change: "+Math.round((x2Hat - x1Hat) * 100.0) / 100.0+"\t mean1: "+Math.round(x1Hat * 100.0) / 100.0+"\t mean2: "+Math.round(x2Hat * 100.0) / 100.0+"\t s1: "+Math.round(s1 * 100.0) / 100.0+"\t s2: "+Math.round(s2 * 100.0) / 100.0+"\t t0: "+Math.round(t0 * 100.0) / 100.0);
		}	
	}	


	//////////////////////////////////////////////======== version007: mean difference based ========///////////////////////////////////////////////
	public void findStepChangeVersion007(){
		int streamIndex, size=CurrentCostSensorData.size();
		double temp_obs=0;
		int wSize=0, threshold=300, sCount=0, tempIndex=0;
		double sum1=0, sum2=0, mean1=0, mean2=0, diff=0, newSum=0, realDiff=0, newMean=0, tempDiff=0;
		List<Double> stepChangeList = new ArrayList<Double>();
		// startup for 5 readings assuming that an appliance is turned on for at least half a minute.
		slidingWindowStartup=4; // just in case if we want any startup: number of streams will not be considered for step change detection.
		List<Double> tempList = new ArrayList<Double>();
		streamIndex=-1;
		while(streamIndex<size-1){
			// do update things increase size of window by 1 etc.
			++streamIndex;
			if(slidingWindow.size() == slidingWindowStartup){ //window size = m_startup
				tempList.addAll(slidingWindow);
				slidingWindow.clear();
				for (int j = 0; j < slidingWindowStartup-1; j++) {
					slidingWindow.add(tempList.get(j+1).doubleValue()); // move the window by one reading
				}slidingWindow.add((double) CurrentCostSensorData.get(streamIndex).getSenseValue()); // add new reading to sliding window.
				tempList.clear();
				ithIndex = streamIndex+1-slidingWindowStartup/2;
				
				//Re setting variables for calculating mean differences
				wSize=0; sum1=0; sum2=0; mean1=0; mean2=0; diff=0; newSum=0; realDiff=0; newMean=0;
				wSize = slidingWindow.size();

				// 1. calculating sum1 and sum2
				for (int j = 0; j < wSize/2; j++) {
					sum1 += slidingWindow.get(j).doubleValue();
					sum2 += slidingWindow.get(j+wSize/2).doubleValue();
				}						
				// 2. calculating means
				mean1 = sum1 / (wSize/2);
				mean2 = sum2 / (wSize/2);
				diff = Math.abs(mean1 - mean2); // absolute difference between two means
				//				System.out.println("\niTheIndex: "+(i+1)+"\t Date/Time: "+CurrentCostSensorData.get(i).getDateTime()+"\t mean1: "+Math.round(mean1 * 100.0) / 100.0+"\t mean2: "+Math.round(mean2 * 100.0) / 100.0+"\t diff: "+Math.round(diff * 100.0) / 100.0);

				// 3. Check if the mean difference is greater than threshold
				if(diff >= threshold){
					tempIndex = streamIndex;
					for (int j = 0; j < wSize/2; j++) {	// new sum here
						newSum += (double) CurrentCostSensorData.get(tempIndex).getSenseValue();
						tempIndex++;
					}
					newMean = newSum/(wSize/2);
					realDiff = Math.abs(mean1-newMean);
					if(realDiff >= threshold){
//						System.out.println("iTheIndex: "+(i+1)+"\t Date/Time: "+CurrentCostSensorData.get(i).getDateTime()+"\t mean1: "+Math.round(mean1 * 100.0) / 100.0+"\t newMean: "+Math.round(newMean * 100.0) / 100.0+"\t newDiff: "+Math.round((newMean-mean1) * 100.0) / 100.0);
						// if its appliance on event then add into step Change list i.e. if mean difference is a positive number
						if(newMean-mean1 > 0){ // > 0 because the step change value is positive so an appliance is turned on or changed state to higher
							ApplianceEventList.add(new ApplianceEventQueue(CurrentCostSensorData.get(streamIndex).getDateTime(), streamIndex, mean1, (newMean-mean1)));
							System.out.println("Start Time: "+CurrentCostSensorData.get(streamIndex).getDateTime()+"\t Start Index: "+streamIndex+"\t mean1: "+Math.round(mean1 * 100.0) / 100.0+"\tnewMean: "+Math.round(newMean * 100.0) / 100.0+"\t Change Diff: "+Math.round((newMean-mean1) * 100.0) / 100.0);
						}
						// if the value is in negative that means the appliance is switched off
						if(newMean-mean1 < 0){ // < 0 because the step change value is negative so that means an appliance is turned off or changed state to lower 
							tempDiff = Math.abs(newMean-mean1);
							for (int j = 0; j < ApplianceEventList.size(); j++) {
								if(Math.abs(ApplianceEventList.get(j).getChangeValue()-tempDiff) <= threshold/2 && (ApplianceEventList.get(j).getEndDateTime()==null)){
//									System.out.println("iTheIndex : "+i+"\t End Time: "+CurrentCostSensorData.get(i).getDateTime()+"\t ChangeValue"+(newMean-mean1));
									ApplianceEventList.get(j).setEndDateTime(CurrentCostSensorData.get(streamIndex).getDateTime());
									ApplianceEventList.get(j).setEventEndIndex(streamIndex);
									extractApplianceSignature(ApplianceEventList.get(j).getStartDateTime(), CurrentCostSensorData.get(streamIndex).getDateTime(), ApplianceEventList.get(j).getEventStartIndex(), ApplianceEventList.get(j).getLastStableBaseLine(), ApplianceEventList.get(j).getChangeValue(), (newMean-mean1));
//									ApplianceEventList.remove(j);
//									System.out.println(ApplianceEventList.size());
								}else{
//									System.out.println("Nothing found!!!");
								}
							}
						}
					// now update variables...
						sCount++;
						streamIndex = streamIndex-wSize/2; // set i to 
						slidingWindow.clear();

					}else{
//						System.out.println("Not a true event");
					}
				}				
			}else{ // poplutate the slidingWindow with real power readings...
				temp_obs = (double) CurrentCostSensorData.get(streamIndex).getSenseValue();
				slidingWindow.add(temp_obs);
			}
		}
//		System.err.println("Start Time\t\tEnd Time\t\tsIndex\teIndex\tLstate\tChange");
//
//		for (int j = 0; j < ApplianceEventList.size(); j++) {
//			System.out.println(ApplianceEventList.get(j).getStartDateTime()+"\t"+ApplianceEventList.get(j).getEndDateTime()+"\t"+ApplianceEventList.get(j).getEventStartIndex()+"\t"+ApplianceEventList.get(j).getEventEndIndex()+"\t"+ApplianceEventList.get(j).getLastStableBaseLine()+"\t"+ApplianceEventList.get(j).getChangeValue());
//		}
		System.out.println("Total events detected: "+sCount); // total changes found
		System.out.println("Turn On events inside ApplianceEventList: "+ApplianceEventList.size());		
	}

	public void extractApplianceSignature(Timestamp applianceOnTime, Timestamp applianceOffTime, int eventStartIndex, double valueBeforeEventStart, double eventStartValue, double eventEndValue){

		boolean newEventOccured=false;
		double newEventValue=0;
		double applianceChangeValue = valueBeforeEventStart;
		
		do{
			for (int i = 0; i < ApplianceEventList.size(); i++) { // trick to avoid first change, i=1 instead i=0
				// make a change here... add a second stable state to the new event so that it does not calculate negative values after previous event finished

			if (eventStartIndex == ApplianceEventList.get(i).getEventStartIndex() && (ApplianceEventList.get(i).getEventEndIndex()==0)){
				newEventOccured=true;
				newEventValue=newEventValue+ApplianceEventList.get(i).getChangeValue();
//				System.out.println("New Event: "+newEventValue);
				}
			}
			// before printing check if the feature is a negative value then try with second stable state... keep check until its positive...
			if(newEventOccured){ // if new event happened within this event
//				System.out.println(CurrentCostSensorData.get(streamIndex).getDateTime()+"\t"+ applianceChangeValue);
				System.out.println(CurrentCostSensorData.get(eventStartIndex).getDateTime()+"\t"+(CurrentCostSensorData.get(eventStartIndex).getSenseValue()- applianceChangeValue - newEventValue));

			}else{
//				System.out.println(CurrentCostSensorData.get(streamIndex).getDateTime()+"\t"+ applianceChangeValue);
				System.out.println(CurrentCostSensorData.get(eventStartIndex).getDateTime()+"\t"+(CurrentCostSensorData.get(eventStartIndex).getSenseValue()- applianceChangeValue));
			}
			eventStartIndex++;
		}while(CurrentCostSensorData.get(eventStartIndex).getDateTime().after(applianceOnTime) && CurrentCostSensorData.get(eventStartIndex).getDateTime().before(applianceOffTime));
		
//		for (int i = 0; i < ApplianceEventList.size(); i++) {
//			System.out.println(ApplianceEventList.get(i).getStartDateTime()+"\t"+ApplianceEventList.get(i).getEndDateTime()+"\t"+ApplianceEventList.get(i).getEventStartIndex()+"\t"+ApplianceEventList.get(i).getEventEndIndex()+"\t"+ApplianceEventList.get(i).getLastStableBaseLine()+"\t"+ApplianceEventList.get(i).getChangeValue());
//		}
	}


	// create an event queue which will have multiple timestamp of an event when started and mean (real power) of windowSize/2 of that event, 



	public static void main(String[] args) {
		StepChangeDetection test = new StepChangeDetection();
		test.readFromCSV();
		//		System.out.println(test.CurrentCostSensorData.size());
		//				test.findStepChangeVersion001();
		//		test.findStepChangeVersion002();
		test.findStepChangeVersion006();
	}

}
